/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*
 * Controller for morph shape selection
 */

var KisBpmShapeSelectionCtrl = [
  '$rootScope',
  '$scope',
  '$timeout',
  '$translate',
  function ($rootScope, $scope, $timeout, $translate) {
    $scope.selectedMorphShapes = [];

    $scope.availableMorphShapes = [];

    for (var i = 0; i < $scope.morphShapes.length; i++) {
      if ($scope.morphShapes[i].id != $scope.currentSelectedShape.getStencil().idWithoutNs()) {
        $scope.availableMorphShapes.push($scope.morphShapes[i]);
      }
    }

    // Config for grid
    $scope.gridOptions = {
      data: 'availableMorphShapes',
      enableRowReordering: true,
      headerRowHeight: 28,
      multiSelect: false,
      keepLastSelected: false,
      selectedItems: $scope.selectedMorphShapes,
      columnDefs: [
        { field: 'objectId', displayName: 'Icon', width: 50, cellTemplate: 'editor-app/popups/icon-template.html?version=' + Date.now() },
        { field: 'name', displayName: 'Name' },
      ],
    };

    // Click handler for save button
    $scope.select = function () {
      if ($scope.selectedMorphShapes.length > 0) {
        var MorphTo = ORYX.Core.Command.extend({
          construct: function (shape, stencil, facade) {
            this.shape = shape;
            this.stencil = stencil;
            this.facade = facade;
          },
          execute: function () {
            var shape = this.shape;
            var stencil = this.stencil;
            var resourceId = shape.resourceId;

            // Serialize all attributes
            var serialized = shape.serialize();
            stencil.properties().each(
              function (prop) {
                if (prop.readonly()) {
                  serialized = serialized.reject(function (serProp) {
                    return serProp.name == prop.id();
                  });
                }
              }.bind(this)
            );

            // Get shape if already created, otherwise create a new shape
            if (this.newShape) {
              newShape = this.newShape;
              this.facade.getCanvas().add(newShape);
            } else {
              newShape = this.facade.createShape({
                type: stencil.id(),
                namespace: stencil.namespace(),
                resourceId: resourceId,
              });
            }

            // calculate new bounds using old shape's upperLeft and new shape's width/height
            var boundsObj = serialized.find(function (serProp) {
              return serProp.prefix === 'oryx' && serProp.name === 'bounds';
            });

            var changedBounds = null;

            if (!this.facade.getRules().preserveBounds(shape.getStencil())) {
              var bounds = boundsObj.value.split(',');
              if (parseInt(bounds[0], 10) > parseInt(bounds[2], 10)) {
                // if lowerRight comes first, swap array items
                var tmp = bounds[0];
                bounds[0] = bounds[2];
                bounds[2] = tmp;
                tmp = bounds[1];
                bounds[1] = bounds[3];
                bounds[3] = tmp;
              }
              bounds[2] = parseInt(bounds[0], 10) + newShape.bounds.width();
              bounds[3] = parseInt(bounds[1], 10) + newShape.bounds.height();
              boundsObj.value = bounds.join(',');
            } else {
              var height = shape.bounds.height();
              var width = shape.bounds.width();

              // consider the minimum and maximum size of
              // the new shape

              if (newShape.minimumSize) {
                if (shape.bounds.height() < newShape.minimumSize.height) {
                  height = newShape.minimumSize.height;
                }

                if (shape.bounds.width() < newShape.minimumSize.width) {
                  width = newShape.minimumSize.width;
                }
              }

              if (newShape.maximumSize) {
                if (shape.bounds.height() > newShape.maximumSize.height) {
                  height = newShape.maximumSize.height;
                }

                if (shape.bounds.width() > newShape.maximumSize.width) {
                  width = newShape.maximumSize.width;
                }
              }

              changedBounds = {
                a: {
                  x: shape.bounds.a.x,
                  y: shape.bounds.a.y,
                },
                b: {
                  x: shape.bounds.a.x + width,
                  y: shape.bounds.a.y + height,
                },
              };
            }

            var oPos = shape.bounds.center();
            if (changedBounds !== null) {
              newShape.bounds.set(changedBounds);
            }

            // Set all related dockers
            this.setRelatedDockers(shape, newShape);

            // store DOM position of old shape
            var parentNode = shape.node.parentNode;
            var nextSibling = shape.node.nextSibling;

            // Delete the old shape
            this.facade.deleteShape(shape);

            // Deserialize the new shape - Set all attributes
            newShape.deserialize(serialized);
            /*
             * Change color to default if unchanged
             * 23.04.2010
             */
            if (
              shape.getStencil().property('oryx-bgcolor') &&
              shape.properties['oryx-bgcolor'] &&
              shape.getStencil().property('oryx-bgcolor').value().toUpperCase() == shape.properties['oryx-bgcolor'].toUpperCase()
            ) {
              if (newShape.getStencil().property('oryx-bgcolor')) {
                newShape.setProperty('oryx-bgcolor', newShape.getStencil().property('oryx-bgcolor').value());
              }
            }
            if (changedBounds !== null) {
              newShape.bounds.set(changedBounds);
            }

            if (newShape.getStencil().type() === 'edge' || newShape.dockers.length == 0 || !newShape.dockers[0].getDockedShape()) {
              newShape.bounds.centerMoveTo(oPos);
            }

            if (newShape.getStencil().type() === 'node' && (newShape.dockers.length == 0 || !newShape.dockers[0].getDockedShape())) {
              this.setRelatedDockers(newShape, newShape);
            }

            // place at the DOM position of the old shape
            if (nextSibling) parentNode.insertBefore(newShape.node, nextSibling);
            else parentNode.appendChild(newShape.node);

            // Set selection
            this.facade.setSelection([newShape]);
            this.facade.getCanvas().update();
            this.facade.updateSelection();
            this.newShape = newShape;
          },
          rollback: function () {
            if (!this.shape || !this.newShape || !this.newShape.parent) {
              return;
            }

            // Append shape to the parent
            this.newShape.parent.add(this.shape);
            // Set dockers
            this.setRelatedDockers(this.newShape, this.shape);
            // Delete new shape
            this.facade.deleteShape(this.newShape);
            // Set selection
            this.facade.setSelection([this.shape]);
            // Update
            this.facade.getCanvas().update();
            this.facade.updateSelection();
          },

          /**
           * Set all incoming and outgoing edges from the shape to the new shape
           * @param {Shape} shape
           * @param {Shape} newShape
           */
          setRelatedDockers: function (shape, newShape) {
            if (shape.getStencil().type() === 'node') {
              (shape.incoming || []).concat(shape.outgoing || []).each(function (i) {
                i.dockers.each(function (docker) {
                  if (docker.getDockedShape() == shape) {
                    var rPoint = Object.clone(docker.referencePoint);
                    // Move reference point per percent

                    var rPointNew = {
                      x: (rPoint.x * newShape.bounds.width()) / shape.bounds.width(),
                      y: (rPoint.y * newShape.bounds.height()) / shape.bounds.height(),
                    };

                    docker.setDockedShape(newShape);
                    // Set reference point and center to new position
                    docker.setReferencePoint(rPointNew);
                    if (i instanceof ORYX.Core.Edge) {
                      docker.bounds.centerMoveTo(rPointNew);
                    } else {
                      var absXY = shape.absoluteXY();
                      docker.bounds.centerMoveTo({ x: rPointNew.x + absXY.x, y: rPointNew.y + absXY.y });
                      //docker.bounds.moveBy({x:rPointNew.x-rPoint.x, y:rPointNew.y-rPoint.y});
                    }
                  }
                });
              });

              // for attached events
              if (shape.dockers.length > 0 && shape.dockers.first().getDockedShape()) {
                newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
                newShape.dockers.first().setReferencePoint(Object.clone(shape.dockers.first().referencePoint));
              }
            } else {
              // is edge
              newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
              newShape.dockers.first().setReferencePoint(shape.dockers.first().referencePoint);
              newShape.dockers.last().setDockedShape(shape.dockers.last().getDockedShape());
              newShape.dockers.last().setReferencePoint(shape.dockers.last().referencePoint);
            }
          },
        });

        var stencil = undefined;
        var stencilSets = $scope.editor.getStencilSets().values();

        var stencilId = $scope.selectedMorphShapes[0].id;
        if ($scope.selectedMorphShapes[0].genericTaskId) {
          stencilId = $scope.selectedMorphShapes[0].genericTaskId;
        }

        for (var i = 0; i < stencilSets.length; i++) {
          var stencilSet = stencilSets[i];
          var nodes = stencilSet.nodes();
          for (var j = 0; j < nodes.length; j++) {
            if (nodes[j].idWithoutNs() === stencilId) {
              stencil = nodes[j];
              break;
            }
          }
        }

        if (!stencil) return;

        // Create and execute command (for undo/redo)
        var command = new MorphTo($scope.currentSelectedShape, stencil, $scope.editor);
        $scope.editor.executeCommands([command]);
      }

      $scope.close();
    };

    $scope.cancel = function () {
      $scope.$hide();
    };

    // Close button handler
    $scope.close = function () {
      $scope.$hide();
    };
  },
];
